package org.monster.context.core;

import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.monster.context.core.file.*;
import org.monster.context.reader.WebXmlReader;
import org.monster.context.reader.XmlReader;
import org.monster.context.reader.tag.FilterTag;
import org.monster.context.reader.tag.ServletTag;
import org.monster.context.reader.tag.XmlTag;
import org.monster.context.servlet.ServletContainer;
import org.monster.context.util.URIUtils;

import javax.servlet.http.HttpServlet;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 配置上下文
 *
 * @author Monster
 */
public class ConfigContext {

    /**
     * 日志器
     */
    private static final Logger logger = Logger.getLogger(ConfigContext.class.getName());
    /**
     * 服务者容器
     */
    private static final ServletContainer container = ServletContainer.getContainer();
    /**
     * 过滤器配置缓存
     */
    private static final FilterConfigCache FILTER_CONFIG_CACHE = FilterConfigCache.getFilterConfigCache();
    /**
     * 服务者配置缓存
     */
    private static final ServletConfigCache SERVLET_CONFIG_CACHE = ServletConfigCache.getServletConfigCache();
    /**
     * 网应用文件
     */
    private final WebAppsFile archives = new WebAppsFile();
    /**
     * xml阅读器
     */
    private final XmlReader reader = new WebXmlReader<XmlTag>();
    /**
     * 应用编译器实现
     */
    private final ApplicationClassLoaderImpl loader = new ApplicationClassLoaderImpl();

    /**
     * 获取 上下文配置
     */
    public void getContextConfig() {
        List<File> apps = archives.getAppsUnder();
        for (File file : apps) {
            archives.scanFileUnder(file);
            String appName = URIUtils.endsWithURI(file.getAbsolutePath(), "\\");
            if (archives.isContainsAppFile()) {
                String xmlPath = Constants.APPS_PATH
                        + ApplicationFile.getPathWebXml(appName);
                File webXml = new File(xmlPath);
                if (!webXml.exists()) {
                    xmlPath = Constants.APPS_PATH
                            + ApplicationFile.getPathWebXml2(appName);
                    webXml = new File(xmlPath);
                }
                Element element;
                try {
                    element = reader.getRootElement(webXml);
                    reader.resolveElement(element);
                } catch (DocumentException ex) {
                    Logger.getLogger(ConfigContext.class.getName()).log(
                            Level.SEVERE, null, ex);
                }
                List<XmlTag> tags = reader.getTags();
                List<FilterTag> filterTags = new ArrayList<>();
                List<ServletTag> servletTags = new ArrayList<>();
                for (XmlTag xmlTag : tags) {
                    if (xmlTag instanceof FilterTag) {
                        FilterTag filterTag = (FilterTag) xmlTag;
                        filterTags.add(filterTag);
                    } else if (xmlTag instanceof ServletTag) {
                        ServletTag servletTag = (ServletTag) xmlTag;
                        servletTags.add(servletTag);
                    }
                }
                FILTER_CONFIG_CACHE.addTags(appName, filterTags);
                SERVLET_CONFIG_CACHE.addTags(appName, servletTags);
                // 别忘记清理
                reader.clearElement();
            }
        }
    }

    /**
     * 装载 服务者配置
     *
     * @throws ClassNotFoundException 类未找到异常
     * @throws InstantiationException 类实例化异常
     * @throws IllegalAccessException 非法访问异常
     */
    public void loadServletConfig() throws ClassNotFoundException,
            InstantiationException, IllegalAccessException {
        Map<String, List<ServletTag>> servletTags = SERVLET_CONFIG_CACHE.getTags();
        Set<String> servletKeySet = servletTags.keySet();
        for (String key : servletKeySet) {
            loader.getWebClassLoader(key);
            List<ServletTag> sts = servletTags.get(key);
            for (ServletTag servletTag : sts) {
                Class<?> clazz = loader.loadClass(servletTag.getServletClass());
                Object obj;
                if (clazz != null) {
                    obj = clazz.newInstance();
                    if (clazz.getSuperclass().getName()
                            .equals("javax.servlet.http.HttpServlet")) {
                        HttpServlet servlet = (HttpServlet) obj;
                        container.addServlet(
                                key + "_" + servletTag.getServletClass(),
                                servlet);
                    }
                }
            }
        }
    }

    /**
     * 清除配置
     */
    public void clearConfig() {
        FILTER_CONFIG_CACHE.clearFilters();
        FILTER_CONFIG_CACHE.clearTags();
        SERVLET_CONFIG_CACHE.clearTags();
        container.clearServlets();
    }

    /**
     * 未初始化 过滤器
     */
    public void notInitFilter() {
        try {
            getContextConfig();
            FilterContext context = new FilterContext();
            context.execute(loader, FILTER_CONFIG_CACHE, FilterNormalPattern.class);
            loadServletConfig();
        } catch (ClassNotFoundException | InstantiationException
                | IllegalAccessException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
    }

    /**
     * 初始化 过滤器
     */
    public void initFilter() {
        try {
            getContextConfig();
            FilterContext context = new FilterContext();
            context.execute(loader, FILTER_CONFIG_CACHE, FilterInitPattern.class);
            loadServletConfig();
        } catch (ClassNotFoundException | IllegalAccessException
                | InstantiationException | IllegalArgumentException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
    }

    /**
     * 初始化 目录
     */
    public void initDirectory() {
        FileMonitor monitor = new FileMonitor(new File(Constants.APPS_PATH),
                archives, new FileSet(), new FileInvoker(),
                new FileCatalog());
        monitor.start(new TimeStep());
    }
}
